/**
 * README
 * 
 * Modificacion de la biblioteca jWave disponible en http://code.google.com/p/jwave/
 * Convertidos los tipos de datos a float para agilizar el tratamiendo de grandes arrays de datos
 * como son los que se tratan en analisis genomicos.
 * Los autores originales de las funciones y del código de la biblioteca cuando fue seleccionada en
 * Abril de 2012 eran:
 *        Christian Scheiblich
 *        Thomas Leduc
 *        sashi
 *        Pol Kennel
 *        Thomas Haider
 *        
 * Por supuesto, todo el merito, honores y demas menciones sobre esta adaptacion les corresponden a ellos
 * y nada mas que a ellos.
 * 
 * 
 * This is a modified version of jWave, available in http://code.google.com/p/jwave/
 * It has been replace the double datatype with float for performance reasons in very big arrays used in
 * bioinformatics like genomic analysis.
 * Original authors of functions and the library when it has been selected in 2012's April were:
 *        Christian Scheiblich
 *        Thomas Leduc
 *        sashi
 *        Pol Kennel
 *        Thomas Haider
 *        
 * Of course, all the merits and mentions about this adaptation have corresponded to them.
 * 
 */

package ev.maths;

/**
 * Una clase que representa un número complejo. </br>
 * Cuando se opera con estos Complex se crea uno nuevo para almacenar el resultado.
 * 
 * @date 01.08.2012
 * @author Enrique Vázquez de Luis
 */
public class Complex {

  /**
   * El número real
   */
  private double real;

  /**
   * El número imaginario
   */
  private double imaginario;

  /**
   * Constructor estándar.
   * 
   * @date 01.08.2012
   * @author Enrique Vázquez de Luis
   */
  public Complex( ) {
    real = 0.;
    imaginario = 0.;
  } // Complex

  /**
   * Constructor copia.
   * 
   * @date 01.08.2012
   * @author Enrique Vázquez de Luis
   * @param c Número complejo que debe copiar.
   */
  public Complex( Complex c ) {
    real = c.real;
    imaginario = c.imaginario;
  } // Complex

  /**
   * Constructor con parámetros real e imaginario.
   * 
   * @date 01.08.2012
   * @author Enrique Vázquez de Luis
   * @param r El número real.
   * @param j El número imaginario.
   */
  public Complex( double r, double j ) {
    real = r;
    imaginario = j;
  } // Complex

  /**
   * Display the current Complex as a String, for usage in println( ) and
   * elsewhere.
   * 
   * @date 01.08.2012
   * @author Enrique Vázquez de Luis
   */
  @Override
  public String toString( ) {
    StringBuffer sb = new StringBuffer( ).append( real );
    if( imaginario > 0 )
      sb.append( '+' ); // else append(i) appends - sign
    return sb.append( imaginario ).append( 'j' ).toString( );
  } // toString

  /**
   * Devuelve el número real.
   * 
   * @date 01.08.2012
   * @author Enrique Vázquez de Luis
   * @return El número real.
   */
  public double getReal( ) {
    return real;
  } // getReal( )

  /**
   * Devuelve el número imaginario.
   * 
   * @date 01.08.2012
   * @author Enrique Vázquez de Luis
   * @return El número imaginario.
   */
  public double getImag( ) {
    return imaginario;
  } // getImag

  /**
   * Cambia el número real.
   * 
   * @date 01.08.2012
   * @author Enrique Vázquez de Luis
   * @param r El nuevo número real.
   */
  public void setReal( double r ) {
    real = r;
  } // setReal

  /**
   * Cambia el número imaginario.
   * 
   * @date 01.08.2012
   * @author Enrique Vázquez de Luis
   * @param j El nuevo número imaginario.
   */
  public void setImag( double j ) {
    imaginario = j;
  } // setImag

  /**
   * Suma un número real al Complex
   * 
   * @date 01.08.2012
   * @author Enrique Vázquez de Luis
   * @param r El real a sumar.
   */
  public void addReal( double r ) {
    real += r;
  } // addReal

  /**
   * Suma un número imaginario al Complex
   * 
   * @date 01.08.2012
   * @author Enrique Vázquez de Luis
   * @param j El imaginario a sumar
   */
  public void addImag( double j ) {
    imaginario += j;
  } // addImag

  /**
   * Multiplica el número real por un escalar.
   * 
   * @date 01.08.2012
   * @author Enrique Vázquez de Luis
   * @param s El escalar a multiplicar.
   */
  public void mulReal( double s ) {
    real *= s;
  } // mulReal

  /**
   * Multiplica el imaginario por un escalar.
   * 
   * @date 01.08.2012
   * @author Enrique Vázquez de Luis
   * @param s El escalar a multiplicar.
   */
  public void mulImag( double s ) {
    imaginario *= s;
  } // mulImag

  /**
   * Calcula el módulo del número complejo.
   * 
   * @date 01.08.2012
   * @author Enrique Vázquez de Luis
   * @return El módulo del numero complejo.
   */
  public double getMag( ) {
    return Math.sqrt( real * real + imaginario * imaginario );
  } // getMag( )

  /**
   * Calcula el argumento (ángulo phi) del número complejo.
   * 
   * @date 01.08.2012
   * @author Enrique Vázquez de Luis
   * @return El argumento del número complejo.
   */
  public double getPhi( ) {
    if( real == 0. && imaginario == 0 )
      return 0.;
    double phi = Math.toDegrees( Math.atan( Math.abs( imaginario / real ) ) );
    if( real >= 0. && imaginario >= 0. ) // 1. quadrant
      return phi;
    if( real <= 0. && imaginario >= 0. ) // 2. quadrant
      return 180. - phi;
    if( real <= 0. && imaginario <= 0. ) // 3. quadrant
      return phi + 180.;
    if( real >= 0. && imaginario <= 0. ) // 4. quadrant
      return 360. - phi;
    return Math.toDegrees( Math.atan( Math.abs( imaginario / real ) ) );
  } // getPhi( )

  /**
   * Devuelve los valores del Complex en un array [ real, imag ].
   * 
   * @date 01.08.2012
   * @author Enrique Vázquez de Luis
   * @return returns Los valores [ real, imag ].
   */
  public double[ ] toArr( ) {
    double[ ] arr = { real, imaginario };
    return arr;
  } // toArr

  /**
   * Devuelve el conjugado del número.
   * 
   * @date 01.08.2012
   * @author Enrique Vázquez de Luis
   * @return Un nuevo Complex con el conjugado.
   */
  public Complex conjugate( ) {
    return new Complex( real, -imaginario );
  } // conjugate

  /**
   * Suma otro Complex a este.
   * 
   * @date 01.08.2012
   * @author Enrique Vázquez de Luis
   * @param c El número a sumar
   * @return Un nuevo Complex con el resultado
   */
  public Complex suma( Complex c ) {
    return new Complex( real + c.real, imaginario + c.imaginario );
  } // add

  /**
   * Resta un Complex al actual.
   * 
   * @date 01.08.2012
   * @author Enrique Vázquez de Luis
   * @param c El Complex a restar.
   * @return Nuevo Complex con el resultado.
   */
  public Complex sub( Complex c ) {
    return new Complex( real - c.real, imaginario - c.imaginario );
  } // sub

  /**
   * Multiplica este Complex por otro.
   * 
   * @date 01.08.2012
   * @author Enrique Vázquez de Luis
   * @param c El complejo por el que multiplicar el actual.
   * @return Nuevo Complex con el resultado
   */
  public Complex mul( Complex c ) {
    return new Complex( real * c.real - imaginario * c.imaginario, real * c.imaginario + imaginario * c.real );
  } // mul

  /**
   * Multiplica este Complex por un escalar.
   * 
   * @date 01.08.2012
   * @author Enrique Vázquez de Luis
   * @param s El escalar por el que multiplicar.
   * @return Nuevo Complex con el resultado.
   */
  public Complex mul( double s ) {
    return new Complex( real * s, imaginario * s );
  } // mul

  /**
   * Divide este complejo por otro.
   * 
   * @date 01.08.2012
   * @author Enrique Vázquez de Luis
   * @param c El complejo por el que dividir.
   * @return Nuevo Complex con el resultado.
   */
  public Complex div( Complex c ) {
    return mul( c.conjugate( ) ).div( c.real * c.real + c.imaginario * c.imaginario );
  } // div

  /**
   * Divide este Complex por un escalar.
   * 
   * @date 01.08.2012
   * @author Enrique Vázquez de Luis
   * @param s El escalar para dividir.
   * @return Nuevo Complex con el resultado
   */
  public Complex div( double s ) {
    return mul( 1. / s );
  } // div

  /**
   * Genera un código Hash para este objeto
   * 
   * @date 01.08.2012
   * @author Enrique Vázquez de Luis
   * @see java.lang.Object#hashCode()
   */
  @Override
  public int hashCode( ) {
    final int prime = 31;
    int result = 1;
    long temp;
    temp = Double.doubleToLongBits( imaginario );
    result = prime * result + (int)( temp ^ ( temp >>> 32 ) );
    temp = Double.doubleToLongBits( real );
    result = prime * result + (int)( temp ^ ( temp >>> 32 ) );
    return result;
  } // hashCode  

  /**
   * Compara este complejo con otro.
   * 
   * @date 01.08.2012
   * @author Enrique Vázquez de Luis
   * @see java.lang.Object#equals(java.lang.Object)
   */
  @Override
  public boolean equals( Object obj ) {
    if( this == obj )
      return true;
    if( obj == null )
      return false;
    if( getClass( ) != obj.getClass( ) )
      return false;
    Complex other = (Complex)obj;
    if( Double.doubleToLongBits( imaginario ) != Double.doubleToLongBits( other.imaginario ) )
      return false;
    if( Double.doubleToLongBits( real ) != Double.doubleToLongBits( other.real ) )
      return false;
    return true;
  } // equals

  /**
   * Imprime el Complex por consola.
   * 
   * @date 01.08.2012
   * @author Enrique Vázquez de Luis
   */
  public void show( ) {
    if( imaginario < 0 )
      System.out.println( getReal( ) + " - j" + Math.abs( getImag( ) ) );
    else
      System.out.println( getReal( ) + " + j" + getImag( ) );
  } // show

  /**
   * Imprime el Complex por consola con un identificador delante.
   * 
   * @date 01.08.2012
   * @author Enrique Vázquez de Luis
   * @param ident Cadena identificadora.
   */
  public void show( String ident ) {
    if( imaginario < 0 )
      System.out.println( ident + ": " + getReal( ) + " - j"
          + Math.abs( getImag( ) ) );
    else
      System.out.println( ident + ": " + getReal( ) + " + j" + getImag( ) );
  } // show

  /**
   * Imprime el módulo por consola.
   * 
   * @date 01.08.2012
   * @author Enrique Vázquez de Luis
   */
  public void showMag( ) {
    System.out.println( getMag( ) );
  } // showMag

  /**
   * Imprime el argumento (ángulo) por consola
   * 
   * @date 01.08.2012
   * @author Enrique Vázquez de Luis
   */
  public void showPhi( ) {
    System.out.println( getPhi( ) );
  } // showPhi

} // class
